using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;

namespace Framework
{
    public static class Is<T>
    {
        #region InList
        /// <summary>
        /// Determines whether [the specified search value] [is in list].
        /// </summary>
        /// <param name="searchValue">The search value.</param>
        /// <param name="list">The list.</param>
        /// <returns>
        /// 	<c>true</c> if [the specified search value] [is in list] ; otherwise, <c>false</c>.
        /// </returns>
        public static bool InList(T searchValue, params T[] list)
        {
            foreach (var value in list)
            {
                if (value.Equals(searchValue))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Determines whether [the specified search value] [is in list].
        /// </summary>
        /// <param name="searchValue">The search value.</param>
        /// <param name="list">The list.</param>
        /// <returns>
        /// 	<c>true</c> if [the specified search value] [is in list] ; otherwise, <c>false</c>.
        /// </returns>
        public static bool InList(T searchValue, IList<T> list)
        {
            foreach (var value in list)
            {
                if (value.Equals(searchValue))
                {
                    return true;
                }
            }
            return false;
        }
        #endregion
    }

	public static class Is
	{
		#region EmptyString
        /// <summary>
        /// Empty String tests a String to see if it is null or empty.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>boolean true if empty.///</returns>
        /// <remarks>Deprecated, use Empty instead</remarks>
        public static bool EmptyString(string value)
        {
            return (value == null || value.Trim().Length == 0);
        }
		#endregion

		#region NotEmptyString
        /// <summary>
        /// Not Empty String tests a String to see if it is NOT null or empty.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>boolean true if not empty.</returns>
        /// <remarks>Deprecated, use NotEmpty instead</remarks>
        public static bool NotEmptyString(string value)
        {
            return !EmptyString(value);
        }
		#endregion

		#region EmptyArray
        /// <summary> Empty Array tests an to see if it is null or has zero elements.
        /// </summary>
        /// <param name="value">Array to be tested</param>
        /// <returns> boolean true if empty.
        /// </returns>
        public static bool EmptyArray(object[] value)
        {
            return (value == null || value.Length == 0);
        }

        /// <summary> Empty Array tests an to see if it is null or has zero elements.
        /// </summary>
        /// <param name="value">Array to be tested</param>
        /// <returns> boolean true if empty.
        /// </returns>
        public static bool EmptyArray(string[] value)
        {
            return (value == null || value.Length == 0);
        }

        /// <summary> Empty Array tests an to see if it is null or has zero elements.
        /// </summary>
        /// <param name="value">Array to be tested</param>
        /// <returns> boolean true if empty.
        /// </returns>
        public static bool EmptyArray(char[] value)
        {
            return (value == null || value.Length == 0);
        }

        /// <summary> Empty Array tests an to see if it is null or has zero elements.
        /// </summary>
        /// <param name="value">Array to be tested</param>
        /// <returns> boolean true if empty.
        /// </returns>
        public static bool EmptyArray(int[] value)
        {
            return (value == null || value.Length == 0);
        }
        /// <summary> Empty Array tests an to see if it is null or has zero elements.
        /// </summary>
        /// <param name="value">Array to be tested</param>
        /// <returns> boolean true if empty.
        /// </returns>
        public static bool EmptyArray(float[] value)
        {
            return (value == null || value.Length == 0);
        }
        /// <summary> Empty Array tests an to see if it is null or has zero elements.
        /// </summary>
        /// <param name="value">Array to be tested</param>
        /// <returns> boolean true if empty.
        /// </returns>
        public static bool EmptyArray(bool[] value)
        {
            return (value == null || value.Length == 0);
        }

        /// <summary> Empty Array tests an to see if it is null or has zero elements.
		/// </summary>
		/// <param name="value">Array to be tested</param>
		/// <returns> boolean true if empty.
		/// </returns>
		public static bool EmptyArray<T>(List<T> value)
		{
			return (value == null || value.Count == 0);
		}
		#endregion

		#region NotEmptyArray
		/// <summary> Not Empty Array tests an to see if an array is NOT null and has one or more elements.
		/// </summary>
		/// <param name="value">Array to be tested</param>
		/// <returns> boolean true if not empty.
		/// </returns>
		public static bool NotEmptyArray(object[] value)
		{
			return (value != null && value.Length > 0);
		}

        /// <summary> Not Empty Array tests an to see if an array is NOT null and has one or more elements.
        /// </summary>
        /// <param name="value">Array to be tested</param>
        /// <returns> boolean true if empty.
        /// </returns>
        public static bool NotEmptyArray<T>(List<T> value)
        {
            return (value != null && value.Count > 0);
        }
        #endregion

		#region EmptyHttpCookie
		/// <summary> Empty HttpCookie tests a HttpCookie to see if it is null or empty.
		/// </summary>
		/// <param name="HttpCookie">HttpCookie to be tested.
		/// </param>
		/// <returns> boolean true if empty.
		/// </returns>
		public static bool EmptyHttpCookie(System.Web.HttpCookie value)
		{
			return (value == null || value.Value == null || value.Value.Trim().Length == 0);
		}
		#endregion

		#region EmptyDateTime
		/// <summary>
		/// A DateTime is considered empty if it is equal to the DateTime.MinValue or
		/// the SqlDateTime.MinValue ( 1/01/0001 12:00:00 AM and 1/01/1753 12:00:00 AM )
		/// respectively.
		/// </summary>
		/// <param name="dateTime">Datetime to check</param>
		/// <returns>boolean</returns>
		public static bool EmptyDateTime(DateTime dateTime)
		{
			return dateTime.Equals(DateTime.MinValue);
		}
		#endregion

		#region EmptySqlDateTime
		/// <summary>
		/// A SqlDateTime is considered empty if it is equal to the SqDateTime.MinValue or
		/// the SqlDateTime.MinValue ( 1/01/0001 12:00:00 AM and 1/01/1753 12:00:00 AM )
		/// respectively.
		/// </summary>
		/// <param name="dateTime">Datetime to check</param>
		/// <returns>boolean</returns>
		public static bool EmptySqlDateTime(SqlDateTime dateTime)
		{
			return (bool)(dateTime.Equals(SqlDateTime.Null) || dateTime.Equals(SqlDateTime.MinValue));
		}
		#endregion

		#region EqualString
		/// <summary>
		/// Test if two strings are equal, by default this will ignore case. They are considered equal if they are both null.
		/// </summary>
		/// <remarks>Deprecated, use Equal(string, string) instead</remarks>
		/// <param name="value1">String 1</param>
		/// <param name="value2">String 2</param>
		/// <returns>boolean</returns>
		public static bool EqualString(string value1, string value2)
		{
			return EqualString(value1, value2, FrameworkConstant.DEFAULT_COMPARISON_IGNORECASE);
		}

		/// <summary>
		/// Test if two strings are equal, by default this will ignore case. They are considered equal if they are both null.
		/// </summary>
		/// <remarks>Deprecated, use Equal(string, string) instead</remarks>
		/// <param name="value1"></param>
		/// <param name="value2"></param>
		/// <param name="stringComparison"></param>
		/// <returns></returns>
		public static bool EqualString(string value1, string value2, StringComparison stringComparison)
		{
            if (value1 == null || value2 == null)
            {
                return value1 == null && value2 == null;
            }

            return value1.Equals(value2, stringComparison);
        }
		#endregion

		#region NotEqualString
		/// <summary>
		/// Test if two strings are NOT equal, by default this will ignore case.
		/// </summary>
		/// <param name="value1"></param>
		/// <param name="value2"></param>
		/// <returns></returns>
        //public static bool NotEqual(string value1, string value2)
        //{
        //    return !Equal(value1, value2);
        //}

		/// <summary>
		/// Test if two strings are NOT equal, Use StringCompariso to set case/culture sensitivity
		/// </summary>
		/// <param name="value1"></param>
		/// <param name="value2"></param>
		/// <param name="stringComparison"></param>
		/// <returns></returns>
        //public static bool NotEqual(string value1, string value2, StringComparison stringComparison)
        //{
        //    return !Equal(value1, value2, stringComparison);
        //}

		/// <summary>
		/// Test if two strings are NOT equal, by default this will ignore case.
		/// </summary>
		/// <remarks>Deprecated, use NotEqual(string, string) instead</remarks>
		/// <param name="value1"></param>
		/// <param name="value2"></param>
		/// <returns></returns>
		public static bool NotEqualString(string value1, string value2)
		{
			return !EqualString(value1, value2);
		}

		/// <summary>
		/// Test if two strings are NOT equal, Use StringCompariso to set case/culture sensitivity
		/// </summary>
		/// <remarks>Deprecated, use NotEqual(string, string, StringComparison) instead</remarks>
		/// <param name="value1"></param>
		/// <param name="value2"></param>
		/// <param name="stringComparison"></param>
		/// <returns></returns>
		public static bool NotEqualString(string value1, string value2, StringComparison stringComparison)
		{
			return !EqualString(value1, value2, stringComparison);
		}
		#endregion

		#region True
		/// <summary>
		/// Returns true if a string has yes/true/1. Any other value including null returns false
		/// </summary>
		/// <param name="value">true/false string</param>
		/// <returns>bool</returns>
		public static bool True(string value)
		{
			return TrueString(value);
		}

		/// <summary>
		/// Returns true if an int has 1.
		/// </summary>
		/// <param name="value">true/false string</param>
		/// <returns>bool</returns>
		public static bool True(int value)
		{
			return value == 1;
		}

		/// <summary>
		/// Returns true if a byte has 1.
		/// </summary>
		/// <param name="value">true/false string</param>
		/// <returns>bool</returns>
		public static bool True(Byte value)
		{
			return value == 1;
		}
		#endregion

		#region False
		/// <summary>
		/// Returns true if the passed in string contains no/false/0. Any other value including null returns false
		/// </summary>
		/// <param name="value">true/false string</param>
		/// <returns>bool</returns>
		public static bool False(string value)
		{
			return !TrueString(value);
		}

		/// <summary>
		/// Returns false if the passed in value has 0. Any other value including null returns false
		/// </summary>
		/// <param name="value">true/false string</param>
		/// <returns>bool</returns>
		public static bool False(int value)
		{
			return value != 1;
		}
		#endregion

		#region TrueString
		/// <summary>
		/// Returns true if a string has true, on, yes or 1.
		/// </summary>
		/// <param name="value">True string</param>
		/// <returns>bool</returns>
		public static bool TrueString(string value)
		{
			if (value == null || value.Trim().Length == 0)
			{
				return false;
			}

			var v = value.ToLower().Trim();

            return v.Equals("true") || v.Equals("on") || v.Equals("yes") || v.Equals("1") || v.Equals("y");
		}
		#endregion

		#region FalseString
		/// <summary>
		/// Returns true if a string has false, off, no, 0 or -1.
		/// </summary>
		/// <param name="value">true/false string</param>
		/// <returns>bool</returns>
		public static bool FalseString(string value)
		{
			if (value == null || value.Trim().Length == 0)
			{
				return false;
			}

			string v = value.ToLower().Trim();

			return v.Equals("false") || v.Equals("off") || v.Equals("no") || v.Equals("0") || v.Equals("-1");
		}
		#endregion

        public static bool IsNull<T>(T value)
        {
            if (value is ValueType)
            {
                return false;
            }
            return null == (object)value;
        }

        public static bool IsDefault<T>(T value)
        {
            return value.Equals(default(T));
        }

        // *********************************************************************************
        // List / Switch 
        // *********************************************************************************

        #region Switch
        public static string Switch(string searchFor, params string[] matchValuePairs)
        {
            if (EmptyArray(matchValuePairs))
            {
                throw new Exception("Switch requires match/value pairs");
            }

            if (matchValuePairs.Length % 2 != 0)
            {
                throw new Exception("Switch requires even number of match/value pairs");
            }

            for (var i = 0; i < matchValuePairs.Length; i += 2)
            {
                if (EqualString(searchFor, matchValuePairs[i]))
                {
                    return matchValuePairs[i + 1];
                }
            }

            return string.Empty;
        }
        #endregion

        #region IndexOf
        /// <summary>
        /// Case Insensitive IndexOff. Return true if <var>searchFor</var> is found within <var>searchIn</var>
        /// </summary>
        /// <param name="searchIn">String to search in</param>
        /// <param name="searchFor">String to search for</param>
        /// <returns></returns>
        public static bool IndexOf(string searchIn, string searchFor)
        {
            return searchIn.ToLower().IndexOf(searchFor.ToLower()) > -1;
        }
        #endregion

        #region InList
        public static bool InList(string searchValue, List<string> searchList)
        {
            return !EmptyArray(searchList) && searchList.Any(value => EqualString(searchValue, value));
        }

	    public static bool InList(string searchValue, params string[] searchList)
	    {
	        return !EmptyArray(searchList) && searchList.Any(value => EqualString(searchValue, value));
	    }

        public static bool InList(char searchValue, params char[] searchList)
        {
            return searchList != null && searchList.Any(value => searchValue == value);
        }

        public static bool InList<T>(T searchValue, List<T> searchList)
        {
            return !EmptyArray(searchList) && searchList.Any(value => searchValue.Equals(value));
        }

        public static bool InList<T>(T searchValue, T[] searchList)
        {
            return searchList != null && searchList.Length>0 && searchList.Any(value => searchValue.Equals(value));
        }
        #endregion

        #region InDelimitedList
        public static bool InDelimitedList(string searchValue, string delimitedSearchList)
        {
            return InList(searchValue, delimitedSearchList.AsList());
        }

        public static bool InDelimitedList(string searchValue, string delimitedSearchList, char seperator)
        {
            return InList(searchValue, delimitedSearchList.AsList(seperator));
        }
        #endregion

		// *********************************************************************************
		// Date/Time routines
		// *********************************************************************************
 
		#region EqualDate
		/// <summary>
		/// Compares two DateTimes for equality of Date (Day, Month & Year properties only)
		/// </summary>
		/// <param name="date1">Date1 to be compared</param>
		/// <param name="date2">Date2 to be compared</param>
		/// <returns>bool</returns>
		public static bool EqualDate(DateTime date1, DateTime date2)
		{
			return (date1.Day == date2.Day) && (date1.Month == date2.Month) && (date1.Year == date2.Year);
		}
		#endregion

		#region NotEqualDate
		/// <summary>
		/// Compares two DateTimes for NOT equality of Date (Day, Month & Year properties only)
		/// </summary>
		/// <param name="date1">Date1 to be compared</param>
		/// <param name="date2">Date2 to be compared</param>
		/// <returns>bool</returns>
		public static bool NotEqualDate(DateTime date1, DateTime date2)
		{
			return !EqualDate(date1, date2);
		}
		#endregion

		#region EqualTime
		/// <summary>
		/// Compares two DateTimes for equality of Time (Hour, Minute & Second properties only)
		/// </summary>
		/// <param name="date1">Date1 to be compared</param>
		/// <param name="date2">Date2 to be compared</param>
		/// <returns>bool</returns>
		public static bool EqualTime(DateTime date1, DateTime date2)
		{
			return (date1.Hour == date2.Hour) && (date1.Minute == date2.Minute) && (date1.Second == date2.Second);
		}
		#endregion

		#region NotEqualTime
		/// <summary>
		/// Compares two DateTimes for NOT equality of Time (Hour, Minute & Second properties only)
		/// </summary>
		/// <param name="date1">Date1 to be compared</param>
		/// <param name="date2">Date2 to be compared</param>
		/// <returns>bool</returns>
		public static bool NotEqualTime(DateTime date1, DateTime date2)
		{
			return !EqualTime(date1, date2);
		}
		#endregion

		#region EqualDateTime
		/// <summary>
		/// Compares two DateTimes for equality of Date and Time (Day, Month, Year, Hour, Minute & Second properties)
		/// </summary>
		/// <param name="date1">Date1 to be compared</param>
		/// <param name="date2">Date2 to be compared</param>
		/// <returns>bool</returns>
		public static bool EqualDateTime(DateTime date1, DateTime date2)
		{
			return EqualDate(date1, date2) && EqualTime(date1, date2);
		}
		#endregion

		#region NotEqualDateTime
		/// <summary>
		/// Compares two DateTimes for NOT equality of Date and Time (Day, Month, Year, Hour, Minute & Second properties)
		/// </summary>
		/// <param name="date1">Date1 to be compared</param>
		/// <param name="date2">Date2 to be compared</param>
		/// <returns>bool</returns>
		public static bool NotEqualDateTime(DateTime date1, DateTime date2)
		{
			return !EqualDateTime(date1, date2);
		}
		#endregion

		// *********************************************************************************
		// Number routines
		// *********************************************************************************

		#region DecimalParse
		/// <summary>
		/// Is the passed in string considered to be a valid decimal value, empty string will fail.
		/// </summary>
		/// <param name="isTreatEmptyAsParseZero">If true then Empty String will be accepted</param>
		/// <param name="value">String value to be passed</param>
		/// <returns></returns>
		public static bool DecimalParse(string value)
		{
			return DecimalParse(false, value);
		}

		/// <summary>
		/// Is the passed in string considered to be a valid decimal value.
		/// </summary>
		/// <param name="isTreatEmptyAsParseZero">If true then Empty String will be accepted</param>
		/// <param name="value">String value to be passed</param>
		/// <returns></returns>
		public static bool DecimalParse(bool isTreatEmptyStringOk, string value)
		{
			try
			{
				if (isTreatEmptyStringOk && Is.EmptyString(value))
				{
					return true;
				}

				Decimal.Parse(value);

				return true;
			}
			catch (Exception)
			{
				return false;
			}
		}
		#endregion

		#region IntParse
		/// <summary>
		/// Is the passed in string considered to be a valid integer value, empty string will fail.
		/// </summary>
		/// <param name="isTreatEmptyAsParseZero">If true then Empty String will be accepted</param>
		/// <param name="value">String value to be passed</param>
		/// <returns></returns>
		public static bool IntParse(string value)
		{
			return IntParse(false, value);
		}

		/// <summary>
		/// Is the passed in string considered to be a valid integer value.
		/// </summary>
		/// <param name="isTreatEmptyAsParseZero">If true then Empty String will be accepted</param>
		/// <param name="value">String value to be passed</param>
		/// <returns></returns>
		public static bool IntParse(bool isTreatEmptyStringOk, string value)
		{
			try
			{
				if (isTreatEmptyStringOk && Is.EmptyString(value))
				{
					return true;
				}

				Int32.Parse(value);

				return true;
			}
			catch (Exception)
			{
				return false;
			}
		}
		#endregion

        // *********************************************************************************
        // Between/Outside
        // *********************************************************************************

        #region Between
        public static bool Between<T>(this T item, T start, T end, bool inclusive = false) where T : IComparable
        {
            return inclusive
                       ? item.CompareTo(start) >= 0 && item.CompareTo(end) <= 0
                       : item.CompareTo(start) > 0 && item.CompareTo(end) < 0;
        }
        #endregion

        #region Outside
        public static bool Outside<T>(this T item, T start, T end, bool inclusive = false) where T : IComparable
        {
            return inclusive
                       ? item.CompareTo(start) <= 0 || item.CompareTo(end) >= 0
                       : item.CompareTo(start) < 0 || item.CompareTo(end) > 0;
        }
        #endregion
    }
}
